package com.blood.core.util.bean;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.util.StringUtils;

import javax.validation.constraints.NotNull;
import java.beans.PropertyDescriptor;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author yelong
 * @date 2020/12/2 10:30
 * @description 继承spring的Bean工具, 并实现自己的一些逻辑处理(不会改变原有值)
 * @Copyright
 */
@Slf4j
public class BeanUtil extends BeanUtils {

    /**
     * 动态为bean添加新的属性和值
     */
    public static Object dynamicBean(Object oldBean, Map<String, Object> newPropertyMap) {
        if (newPropertyMap == null || newPropertyMap.isEmpty()) {
            return oldBean;
        } else {
            // bean生成工具
            BeanGenerator generator = new BeanGenerator();
            // 设置继承类
            generator.setSuperclass(oldBean.getClass());
            // 添加新的属性
            Map<String, Class> propertyMap = new HashMap<>();
            newPropertyMap.forEach((key, value) -> propertyMap.put(key, value.getClass()));
            BeanGenerator.addProperties(generator, propertyMap);
            // 创建新的bean
            BeanMap beanMap = BeanMap.create(generator.create());
            // 为新的bean赋值
            newPropertyMap.forEach(beanMap::put);
            // 返回后需要把旧的值拷贝过来
            Object newBean = beanMap.getBean();
            copyProperties(oldBean, newBean);
            return newBean;
        }
    }

    public static void copyProperties(@NotNull Object source, @NotNull Object target) {
        BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
    }

    public static <T> T copyProperties(@NotNull Object source, @NotNull Class<T> clazz) {
        T target;
        try {
            target = clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            String message = MessageFormat.format("{0} 对象无法实例化", clazz);
            log.error(message, e);
            throw new RuntimeException(message);
        }
        BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
        return target;
    }

    public static <T> T copyProperties(@NotNull Object source, @NotNull Class<T> clazz, String... ignoreProperties) {
        T target;
        try {
            target = clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            String message = MessageFormat.format("{0} 对象无法实例化", clazz);
            log.error(message, e);
            throw new RuntimeException(message);
        }

        BeanUtils.copyProperties(source, target, ArrayUtils.addAll(getNullPropertyNames(source), ignoreProperties));
        return target;
    }

    /**
     * 获取bean对象中值为空的属性名称
     */
    public static String[] getNullPropertyNames(@NotNull Object source) {
        BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();
        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (StringUtils.isEmpty(srcValue)) {
                emptyNames.add(pd.getName());
            }
        }
        int size = emptyNames.size();
        return emptyNames.toArray(new String[size]);
    }
}
